#include "timer.h"

#include "lpc_types.h"
#include "lpc17xx_timer.h"
/* Private defines -------------------------------------------------------- */

/* Private flags ---------------------------------------------------------- */
uint8_t timer0_set = 0, timer1_set = 0, timer2_set = 0, timer3_set = 0;
uint8_t timer_init;
uint16_t timer_sequence = 0;

/* Private globals -------------------------------------------------------- */
void (*interrupt_vector[4])(void) = { NULL, NULL, NULL, NULL };
uint16_t sequence_value[4] = {0, 0, 0, 0};
TIM_TIMERCFG_Type TIM_Conf;
TIM_MATCHCFG_Type TIM_Match;

/* Timer Interrupts ------------------------------------------------------- */

void TIMER0_IRQHandler()
{
    if(TIM_GetIntStatus(LPC_TIM0, TIM_MR0_INT) == SET)
    {
        if (timer0_set)
            interrupt_vector[0]();
    }
    // Clear Interupt
    TIM_ClearIntPending(LPC_TIM0, TIM_MR0_INT);
    return;
}

void TIMER1_IRQHandler()
{
    if(TIM_GetIntStatus(LPC_TIM1, TIM_MR1_INT) == SET)
    {
        if (timer1_set)
            interrupt_vector[1]();
    }
    // Clear Interupt
    TIM_ClearIntPending(LPC_TIM1, TIM_MR1_INT);
    return;
}

void TIMER2_IRQHandler()
{
    if(TIM_GetIntStatus(LPC_TIM2, TIM_MR2_INT) == SET)
    {
        if (timer2_set)
            interrupt_vector[2]();
    }
    // Clear Interupt
    TIM_ClearIntPending(LPC_TIM2, TIM_MR2_INT);
    return;
}

void TIMER3_IRQHandler()
{
    if(TIM_GetIntStatus(LPC_TIM3, TIM_MR3_INT) == SET)
    {
        if (timer3_set)
            interrupt_vector[3]();
    }
    // Clear Interupt
    TIM_ClearIntPending(LPC_TIM3, TIM_MR3_INT);
    return;
}

/* Init functions --------------------------------------------------------- */
void init_timer(void)
{
    if (timer_init)
        return;
    
    // Timer Configuration
    TIM_Conf.PrescaleOption = TIM_PRESCALE_USVAL;
    
    // Timer Match Setup
    TIM_Match.IntOnMatch            = TRUE;
    TIM_Match.ResetOnMatch          = TRUE;
    TIM_Match.StopOnMatch           = FALSE;
    TIM_Match.ExtMatchOutputType    = TIM_EXTMATCH_NOTHING;
    
    // Set Priority
    NVIC_SetPriority(TIMER0_IRQn, 5);
    NVIC_SetPriority(TIMER1_IRQn, 5);
    NVIC_SetPriority(TIMER2_IRQn, 5);
    NVIC_SetPriority(TIMER3_IRQn, 5);
    
    // Init flags
    timer0_set = 0;
    timer1_set = 0;
    timer2_set = 0;
    timer3_set = 0;
    timer_init = 1;
    
    return;
    
}

/* Init functions --------------------------------------------------------- */
void deinit_timer(void)
{
    if (!timer_init)
        return;
    
    if (timer0_set)
        disable_timer(sequence_value[0]);
    if (timer1_set)
        disable_timer(sequence_value[1]);
    if (timer2_set)
        disable_timer(sequence_value[2]);
    if (timer3_set)
        disable_timer(sequence_value[3]);
            
    timer_init = 0;
    timer_sequence = 0;
    
    return;
}

/* Data Accessors --------------------------------------------------------- */
uint16_t enable_timer(uint32_t prescale, uint32_t match, void (*func)(void))
{
    if (!timer_init)
        return 0;
    if (!timer0_set)
    {
        TIM_Conf.PrescaleValue = prescale;
        TIM_Match.MatchValue = match;
        TIM_Match.MatchChannel = 0;
        interrupt_vector[0] = func;
        sequence_value[0] = ++timer_sequence;
        
        TIM_Init(LPC_TIM0, TIM_TIMER_MODE, &TIM_Conf);
        TIM_ConfigMatch(LPC_TIM0, &TIM_Match);
        TIM_Cmd(LPC_TIM0, ENABLE);
        NVIC_EnableIRQ(TIMER0_IRQn);
        
        timer0_set = 1;
        return timer_sequence;
    }
    if (!timer1_set)
    {
        TIM_Conf.PrescaleValue = prescale;
        TIM_Match.MatchValue = match;
        TIM_Match.MatchChannel = 1;
        interrupt_vector[1] = func;
        sequence_value[1] = ++timer_sequence;
        
        TIM_Init(LPC_TIM1, TIM_TIMER_MODE, &TIM_Conf);
        TIM_ConfigMatch(LPC_TIM1, &TIM_Match);
        TIM_Cmd(LPC_TIM1, ENABLE);
        NVIC_EnableIRQ(TIMER1_IRQn);
        
        timer1_set = 1;
        return timer_sequence;
    }
    if (!timer2_set)
    {
        TIM_Conf.PrescaleValue = prescale;
        TIM_Match.MatchValue = match;
        TIM_Match.MatchChannel = 2;
        interrupt_vector[2] = func;
        sequence_value[2] = ++timer_sequence;
        
        TIM_Init(LPC_TIM2, TIM_TIMER_MODE, &TIM_Conf);
        TIM_ConfigMatch(LPC_TIM2, &TIM_Match);
        TIM_Cmd(LPC_TIM2, ENABLE);
        NVIC_EnableIRQ(TIMER2_IRQn);
        
        timer2_set = 1;
        return timer_sequence;
    }
    if (!timer3_set)
    {
        TIM_Conf.PrescaleValue = prescale;
        TIM_Match.MatchValue = match;
        TIM_Match.MatchChannel = 3;
        interrupt_vector[3] = func;
        sequence_value[3] = ++timer_sequence;
        
        TIM_Init(LPC_TIM3, TIM_TIMER_MODE, &TIM_Conf);
        TIM_ConfigMatch(LPC_TIM3, &TIM_Match);
        TIM_Cmd(LPC_TIM3, ENABLE);
        NVIC_EnableIRQ(TIMER3_IRQn);
        
        timer3_set = 1;
        return timer_sequence;
    }
    return 0;
}

void disable_timer(uint16_t timer)
{
    if ((sequence_value[0] == timer) && timer0_set)
    {
        TIM_DeInit(LPC_TIM0);
        TIM_Cmd(LPC_TIM0, DISABLE);
        NVIC_EnableIRQ(TIMER0_IRQn);
        timer0_set = 0;
        interrupt_vector[0] = NULL;
        sequence_value[0] = 0;
        return;
    }
    if ((sequence_value[1] == timer) && timer1_set)
    {
        TIM_DeInit(LPC_TIM1);
        TIM_Cmd(LPC_TIM1, DISABLE);
        NVIC_EnableIRQ(TIMER1_IRQn);
        timer1_set = 0;
        interrupt_vector[1] = NULL;
        sequence_value[1] = 0;
        return;
    }
    if ((sequence_value[2] == timer) && timer2_set)
    {
        TIM_DeInit(LPC_TIM2);
        TIM_Cmd(LPC_TIM2, DISABLE);
        NVIC_EnableIRQ(TIMER2_IRQn);
        timer2_set = 0;
        interrupt_vector[2] = NULL;
        sequence_value[2] = 0;
        return;
    }
    if ((sequence_value[3] == timer) && timer3_set)
    {
        TIM_DeInit(LPC_TIM3);
        TIM_Cmd(LPC_TIM3, DISABLE);
        NVIC_EnableIRQ(TIMER3_IRQn);
        timer3_set = 0;
        interrupt_vector[3] = NULL;
        sequence_value[3] = 0;
        return;
    }
    return;
}
